package org.lex.game.sudoku.model.solver;

import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;

public class DefaultSolverObserver implements SolverObserver {
	private Deque<Decision> decisions = new ArrayDeque<>();
	private final PrintWriter out;

	public DefaultSolverObserver() {
		this(new PrintWriter(System.out));
	}

	public DefaultSolverObserver(PrintWriter out) {
		this.out = out;
	}

	@Override
	public void beginDecision(Decision d) {
		StringBuilder buf = new StringBuilder();
		this.indent(buf);
		buf.append("Begin decision at (").append(d.row + 1).append(", ").append(d.column + 1).append(")=")
				.append(d.number);
		out.println(buf);
		this.decisions.addLast(d);
		this.updateDecisions();
	}

	@Override
	public void fillCell(int row, int col, int num) {
		StringBuilder buf = new StringBuilder();
		this.indent(buf);
		buf.append("filled cell (").append(row + 1).append(", ").append(col + 1).append(")=").append(num);
		out.println(buf);
	}

	@Override
	public void rollbackDecision(Decision d, int cellCount) {
		this.decisions.removeLast();
		StringBuilder buf = new StringBuilder();
		this.indent(buf);
		buf.append("Rollback decision at (").append(d.row + 1).append(", ").append(d.column + 1).append(")=")
				.append(d.number);
		buf.append(", ").append(cellCount).append(" cells rollbacked");
		out.println(buf);
		this.updateDecisions();
	}

	@Override
	public void reset() {
	}

	private void updateDecisions() {
		StringBuilder buf = new StringBuilder();
		buf.append(this.decisions.size()).append(" Decisions: ");
		for (Iterator<Decision> iter = this.decisions.iterator(); iter.hasNext();) {
			Decision d = iter.next();
			buf.append('(').append(d.row + 1).append(',').append(d.column + 1).append(")=").append(d.number);
			if (iter.hasNext()) {
				buf.append("-> ");
			}
		}
		out.print(">>Decisions: ");
		out.println(buf);
	}

	private StringBuilder indent(StringBuilder buf) {
		for (int i = 0, n = this.decisions.size(); i < n; i++) {
			buf.append('\t');
		}
		return buf;
	}
}
